
@@controllers_mutex = Mutex.new
@@controllers = Hash.new

#require 'find'
#require 'fileutils'

#
# Routing is the core of most web apps.
#
# A little bit about the idea of "routing" in general in applications in general:
#
#   1) An application usually consists of a bunch of event handlers which respond to certain events.
#   2) Routing in the broadest sense partitions the 'namespace' of how urls are handled.
#   3) A common technique is to have a regular expression table for deciding which handler to call.
#   4) Ruby on Rails for example has a regular expression route table that wires up controllers to events.
#   5) Jetty also has a RoR-like pattern where statically mounted regular expressions map to servlets.
# 
# This application also has a similar pattern for doing routing.  In this application:
# 
#   1) You register routers and do your own handler evaluation.
#   2) You could do regular expression based url mapping to controllers or just do procedural evaluation.
#   3) Routers are arranged in a chain and all routers are called until one returns true.
#   4) There is no pre-evaluation or post-evaluation design here.
# 
# This particular file defines a basic route that is meant to be subclassed and it defines some core patterns:
# 
#   1) handlers are named '*controller.rb'
#   2) handlers should have a globally unique class name - this is their own responsibility
#   3) handlers should accept a series of parameters for initialization
#   4) handlers should accept initialization without necessarily doing evaluation
#   5) handlers essentially define part of a 'namespace' so we may poll them without invoking them
# 
# 

class BaseRoute

  #
  # find best dynamic handler by name to load and return it by name or nil
  # intended to be overridden by a subclass
  # TODO offer a regex solution for the lazy
  # TODO this whole thing is a bit verbose and could be much smaller
  #
  def self.get_handlerpath(request,response,requestpath)
    return nil,nil,nil,nil
  end

  #
  # optional utility to build a named handler off disk
  # returns the class (not the instance) or nil
  #
  def self.get_handler(handlerpath)

    path = nil
    Dir["#{handlerpath.join('/')}/*controller.rb"].each do |path|
      # just take the last one... so just iterate over all
    end

    return nil if !path

    handler = @@controllers[path]
    handler = nil if @@debug

    if !handler && File.exists?(path)
      puts "base_route::get_handler() loading controller #{path}"
      # find the name of the controller
      begin
        data = File.new(path).read
      rescue
        return nil
      end
      if !data
        return nil
      end
      if true
        # we are using this approach to finding the controller name
        # one approach to finding the name is to look at the controller name
        # this puts burden of unique naming on developer and is fragile in a multi-developer world
        classname = data.scan(/class (.+Controller)/)[0][0].split(' ')[0]
      else
        # another approach is to manufacture the name
        # this is the camping approach but destroys inheritance and is not the expected behavior
        #classname = handlerpath.collect { |x| x.capitalize }.join
        #classname = self.const_get("#{name}Controller")
        #data.gsub!(/class Controller/,"class #{classname}Controller")
      end

      @@controllers_mutex.synchronize do
        if false
          # xxx i guess i cannot do this unless i unload the superclass
          # xxx eval probably should be deprecated in favor of require
          # xxx also it destroys __FILE__
          # xxx and puts handler in wrong namespace... basically totally useless...
          eval data
        else
          # TODO this approach should be made to unload and reload in case of debugging
          require path
        end
        handler = self.const_get classname
        @@controllers[path] = handler
      end
    end

    return handler

  end

  #
  # invoked by servlet container to resolve requests; return false if no handler found
  #
  def self.resolve(request,response,server)
      
    # 
    # normalize domain names - turn anselm.hook.org into hook/anselm for example
    # (in this architecture we keep application 'controllers' in a sub folder named after domain)
    # 
    domainreversed = request.request_uri.host.split('.').reverse[1..-1]

    # 
    # turn url into an array something like /hook/anselm/see_all_posts
    # 
    requestpath = request.request_uri
    return false if !requestpath
    if requestpath.is_a?(String)
      requestpath = requestpath.split('/')
    elsif !requestpath.is_a?(Array) && requestpath.host && requestpath.path
      # hack - if it is some structured type like a request uri
      requestpath = domainreversed + requestpath.path.split('/')
    else
      raise "Cannot figure out request type"
    end
    requestpath.delete("")

    # misc
    handler = nil
    localdomain = requestpath[0]

    # get best path to a handler { the site developer specifies this )
    handlerpath,workingpath,requestpath = self.get_handlerpath request,response,requestpath
    return false if !handlerpath

    # get handler class if found
    handler = self.get_handler handlerpath
    return false if !handler

    # make that handler
    inst = handler.new

    # run it
    inst.service(request,response,handlerpath,workingpath,requestpath) if inst

    # return success
    return true

  end

  def resolve
    return self.resolve
  end

end
